using System.ComponentModel;

namespace KlearTweet.Client.Infrastructure
{
    public static class ThreadPoolHelper // Run the workerMethod in the ThreadPool and call the completedMethod when it is done on the Main/UI thread
    {
        private class RunData
        {
            public DoWorkEventHandler WorkerMethod { get; set; }
            public RunWorkerCompletedEventHandler CompletedMethod { get; set; }
            public object Argument { get; set; }
        }

        public static void RunAsync(DoWorkEventHandler workerMethod, RunWorkerCompletedEventHandler completedMethod)
        {
            RunAsync(workerMethod, completedMethod, null);
        }

        public static void RunAsync(DoWorkEventHandler workerMethod, RunWorkerCompletedEventHandler completedMethod, object argument)
        {
            // TODO: Tune Min & Max Threads ?!?
            System.Threading.ThreadPool.QueueUserWorkItem(Run, new RunData { WorkerMethod = workerMethod, CompletedMethod = completedMethod, Argument = argument });
        }

        private static void Run(object state)
        {
            var data = (RunData) state;
            object result = null;
            System.Exception error = null;
            if (System.Diagnostics.Debugger.IsAttached) // Run Queued UserWorkItem without try/catch
                RunInBackground(data, out result);
            else
                try
                {
                    RunInBackground(data, out result);
                }
                catch (System.Exception ex)
                {
                    error = ex;
                }

            RunOnMainThread(data.CompletedMethod, new RunWorkerCompletedEventArgs(result, error, false));
        }

        private static void RunInBackground(RunData data, out object result)
        {
            var e = new DoWorkEventArgs(data.Argument);
            data.WorkerMethod(null, e);
            result = e.Result;
        }

        private static void RunOnMainThread(RunWorkerCompletedEventHandler completedMethod, RunWorkerCompletedEventArgs e)
        {
            if (!System.Windows.Deployment.Current.Dispatcher.CheckAccess()) // Check if we are on the UI thread if not switch
                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(
                    new System.Action<RunWorkerCompletedEventHandler, RunWorkerCompletedEventArgs>(RunOnMainThread), completedMethod, e);
            else
                completedMethod(null, e);
        }

        public static void RunOnMainThread(System.Action methodCallback)
        {
            if (!System.Windows.Deployment.Current.Dispatcher.CheckAccess()) // Check if we are on the UI thread if not switch
                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(
                    new System.Action<System.Action>(RunOnMainThread), methodCallback);
            else
                methodCallback();
        }

        public static void RunWhen(int timeout, System.Func<bool> runCondition, System.Action<bool> action)
        {
            var timer = new System.Windows.Threading.DispatcherTimer();
            timer.Tick += (s, ev) =>
            {
                timer.Stop();
                if (runCondition() || timeout <= 0)
                    action(timeout <= 0);
                else // Wait => Reschedule
                    RunWhen(timeout - 50, runCondition, action);
            };
            timer.Interval = new System.TimeSpan(0, 0, 0, 0, 50); // TODO: Tune number
            timer.Start();
        }
    }
}